﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.IO.Packaging;
using Microsoft.Xna.Framework;


namespace Engine.MarchCube
{
    public struct OutResult
    {
        public Color IsoColor;
        public float Iso;    
        public static OutResult operator +(OutResult res1, OutResult res2)
        {
            OutResult res3 = new OutResult();
            res3.Iso = res1.Iso + res2.Iso;
            res3.IsoColor = new Color() { PackedValue = (res1.IsoColor.PackedValue + res2.IsoColor.PackedValue) / 2 };
    if (Math.Abs(res1.Iso - res2.Iso) < 0.00001)
    {
        res3.IsoColor=res1.IsoColor;
     
    }
    else if (Math.Abs(res2.Iso - res1.Iso) < 0.00001)
    {
        res3.IsoColor = res2.IsoColor;

    }
        else
	{
         float   mu = (0.1f - res1.Iso) / (res2.Iso - res1.Iso);
         res3.IsoColor = Color.Lerp(res1.IsoColor, res2.IsoColor, mu);
	}
        
        
           
            return res3;
        }
    }
    public class BaseElement
    {
        BoundingBox m_Bbox;
        public BoundingBox BBox { get { return m_Bbox; } }
        byte[, ,] m_data;
        int m_Size;
        int Count = 0;
        string m_FileName;
        Vector3 m_position;
        
       public Engine.Voxel.IBaseMaterial mat;
        Random rnd;
        public BaseElement(int size,Vector3 pos,string FileName)
        {
            m_data = new byte[size, size, size];
            m_Size = size;
            m_position =pos;
            
            Vector3 Max = pos;
            Max.X += m_Size/2;
            Max.Y += m_Size / 2;
            Max.Z += m_Size / 2;
            Vector3 Min = pos;
            Min.X -= m_Size / 2;
            Min.Y -= m_Size / 2;
            Min.Z -= m_Size / 2;
            m_Bbox = new BoundingBox(Min, Max);
            m_FileName = FileName; 
            mat = Engine.Voxel.Materials.GetMat("Ground");
            rnd = new Random(Environment.TickCount);
        }
        public void RndDat()
        {
           
            Count = 0;
           
            for (int i = 2; i < m_Size-1; i++)
            {
                for (int t = 2; t < m_Size - 1; t++)
                {
                    for (int k =2; k < m_Size - 1; k++)
                    {
                        m_data[i, t, k] = (byte)rnd.Next(1, 255);
                    }
                }
            }
        }
        public BaseElement(string FileName)
        {
            m_FileName = FileName;
            m_data = LoadData();
        }
        public bool Save(string SpecificFile = "")
        {
            string FileName = m_FileName;
            if (SpecificFile != "")
            {
                FileName = SpecificFile;
                m_FileName = SpecificFile;
            }

            if (m_data != null)
            {
                using (Package zip = Package.Open(FileName, FileMode.OpenOrCreate))
                {

                    Uri Head = PackUriHelper.CreatePartUri(new Uri("Header.dat", UriKind.Relative));
                    if (zip.PartExists(Head))
                    {
                        zip.DeletePart(Head);
                    }
                    PackagePart headPart = zip.CreatePart(Head, "", CompressionOption.Normal);
                    byte[] bufer = new byte[16];
                    using (Stream s = headPart.GetStream())
                    {


                        Array.Copy(BitConverter.GetBytes(m_Size), 0, bufer, 0, 4);
                        Array.Copy(BitConverter.GetBytes(m_position.X), 0, bufer, 4, 4);
                        Array.Copy(BitConverter.GetBytes(m_position.Y), 0, bufer, 8, 4);
                        Array.Copy(BitConverter.GetBytes(m_position.Z), 0, bufer, 12, 4);
                        s.Write(bufer, 0, bufer.Length);
                    }
                    //   temp = new byte[m_Size, m_Size, m_Size];
                    for (int y = 0; y < m_Size; y++)
                    {
                        Uri TempDat = PackUriHelper.CreatePartUri(new Uri("y" + y + ".dat", UriKind.Relative));
                        bufer = new byte[m_Size * m_Size];
                        if (zip.PartExists(TempDat))
                        {
                            zip.DeletePart(TempDat);
                        }
                        headPart = zip.CreatePart(TempDat, "", CompressionOption.Normal);

                        using (Stream s = headPart.GetStream())
                        {
                           
                            for (int x = 0; x < m_Size; x++)
                            {
                                for (int z = 0; z < m_Size; z++)
                                {
                                    int index = x * m_Size + z;
                                    bufer[index]=m_data[x, y, z];
                                }
                            }
                            s.Write(bufer, 0, bufer.Length);
                        }
                    }

                    zip.Close();

                }
                
                return true;
            }
            return false;
        }
        public byte this[int x,int y,int z]
        {
            get { return m_data[x, y, z]; }
            set { m_data[x, y, z]=value; }
        }
        public bool load(string SpecificFile = "")
        {
            m_data = LoadData(SpecificFile);
            if (m_data == null)
            {
                return false;
            }
            return true;
        }
        private byte[, ,] LoadData(string SpecificFile = "")
        {
            string FileName = m_FileName;
            if (SpecificFile != "")
            {
                FileName = SpecificFile;
                m_FileName = SpecificFile;
            }


            FileInfo fi = new FileInfo(FileName);
            byte[, ,] temp = null;
            if (fi.Exists)
            {
                using (Package zip = Package.Open(FileName, FileMode.Open))
                {
                    Uri Head = PackUriHelper.CreatePartUri(new Uri("Header.dat", UriKind.Relative));
                    if (zip.PartExists(Head))
                    {
                        PackagePart headPart = zip.GetPart(Head);
                        Stream s = headPart.GetStream();
                        byte[] bufer = new byte[s.Length];
                        s.Read(bufer, 0, bufer.Length);
                        m_Size = BitConverter.ToInt32(bufer, 0);
                        m_position = new Vector3(
                            BitConverter.ToSingle(bufer, 4),
                             BitConverter.ToSingle(bufer, 8),
                              BitConverter.ToSingle(bufer, 12)
                                    );
                        temp = new byte[m_Size, m_Size, m_Size];
                        for (int y = 0; y < m_Size; y++)
                        {
                            Uri TempDat = PackUriHelper.CreatePartUri(new Uri("y" + y + ".dat", UriKind.Relative));
                            headPart = zip.GetPart(TempDat);
                            bufer = new byte[m_Size * m_Size];
                            s = headPart.GetStream();
                            s.Read(bufer, 0, bufer.Length);
                            for (int x = 0; x < m_Size; x++)
                            {
                                for (int z = 0; z < m_Size; z++)
                                {
                                    int index = x * m_Size + z;
                                    temp[x, y, z] = bufer[index];
                                }
                            }
                        }

                    }

                }
            }
            return temp;
        }
        public OutResult chekPos1(float xPos, float yPos, float zPos)
        {
            OutResult res = new OutResult();
          
            
            int xMin = (int)(xPos-m_position.X);
            int yMin = (int)(yPos - m_position.Y);
            int zMin = (int)(zPos - m_position.Z);
            float resD = 0;
            uint PackColor=0;
            uint countCol = 0;
            for (int x = xMin; x <= xMin + 1; x++)
            {
                for (int y = yMin; y <= yMin + 1; y++)
                {
                    for (int z = zMin; z <= zMin + 1; z++)
                    {
                        if (x < m_Size && y < m_Size && z < m_Size
                            &&
                            x>-1 && y>-1 &&z>-1 
                            )
                        {


                           // if (m_data[x, y, z] > 0)
                           // {
                                //resD+= 1f / ((x - xPos) * (x - xPos) + (y - yPos) * (y - yPos) + (z - zPos) * (z - zPos));
                                float xc, yc, zc;
                                xc = x - (xPos - m_position.X)-0.5f;
                                yc = y - (yPos - m_position.Y) - 0.5f;
                                zc = z - (zPos - m_position.Z) - 0.5f;
                                
                                Vector3 center = new Vector3(5, 5, 5);
                              //  BoundingSphere bs = new BoundingSphere(center, 1);
                              
                                Vector3 dir = new Vector3(xPos - m_position.X, yPos - m_position.Y, zPos - m_position.Z);
                               // if (bs.Contains(dir) == ContainmentType.Contains || bs.Contains(dir) == ContainmentType.Intersects)
                              //  {
                                resD += ScalarFunction.Function((ScalarType)3, center, dir,1);
                                 //   float dist = Vector3.Distance(center, dir);
                                  //resD +=1/ ((dist * dist) * (dist * dist));
                                  //  resD+=(1- dist);
                                  if (resD>0)
                                  {
                                    
                                    PackColor= mat.MaterialColors[rnd.Next(0,mat.MaterialColors.Length)].PackedValue;                                    
                                    if (Count >= mat.MaterialColors.Length)
                                    {
                                        Count = 0;
                                    }
                                  }
                              //  }
                               
                               // resD += -((xc * xc + yc * yc + zc * zc)) + (float)Math.Cos(5* xc) + (float)Math.Cos(5* yc) + (float)Math.Cos(5* zc);
                              
                           // }
                        }
                    }
                }
            }
          
            if (resD > 0)
            {
              
                res.Iso = resD;
                res.IsoColor = new Color() { PackedValue = PackColor };
            }
            return res;

        }

        public OutResult chekPos(float xPos, float yPos, float zPos)
        {
            OutResult res = new OutResult();


            int xMin = (int)(xPos - m_position.X);
            int yMin = (int)(yPos - m_position.Y);
            int zMin = (int)(zPos - m_position.Z);
            float resD = 0;
            uint PackColor = 0;
            
            


                          

                            Vector3 center =m_Bbox.Min+ new Vector3(8, 8, 8);
                           

                            Vector3 dir = new Vector3(xPos - m_position.X, yPos - m_position.Y, zPos - m_position.Z);
                            float xc, yc, zc;
                            xc = dir.X;
                            yc = dir.Y;
                            zc = dir.Z;

                            Matrix r = Matrix.CreateScale(m_Size - 1)* Matrix.CreateTranslation(m_position);

                            resD = r.M11 * xc * 2
                                   + r.M22 * yc * 2
                                   + r.M33 * zc * 2
                                      + r.M12 * xc * yc
                                      + r.M13 * xc * zc
                                      + r.M23 * yc * zc
                                      + r.M14 * xc
                                      + r.M24 * yc
                                      + r.M34 * zc
                                      + r.M44;
                          float fr=-3;
                          float xc1, yc1, zc1;
                        
                           center = m_Bbox.Min + new Vector3(1, 1, 1);
                           fr = -3;
                           xc1 = (xPos - center.X);
                           yc1 = (yPos - center.Y);
                           zc1 = (zPos - center.Z);
                           resD += fr *(float)Math.Pow ( 1f/(float)Math.Sqrt(xc1 * xc1 + yc1 * yc1 + zc1 * zc1 - 1),2);
                         //   resD += ScalarFunction.Function((ScalarType)3, center, dir, 1);
                          
                           // if (resD > 0)
                          //  {

                                PackColor = mat.MaterialColors[rnd.Next(0, mat.MaterialColors.Length)].PackedValue;
                                if (Count >= mat.MaterialColors.Length)
                                {
                                    Count = 0;
                                }
                          //  }
           

            if (resD > 0)
            {

                res.Iso = resD;
                res.IsoColor = new Color() { PackedValue = PackColor };
            }
            return res;

        }

        public Color GetColor(double xPos, double yPos, double zPos)
        {
            Color res = Color.Transparent;  
            int xMin = (int)(xPos - m_position.X);
            int yMin = (int)(yPos - m_position.Y);
            int zMin = (int)(zPos - m_position.Z);
            Vector4 resD=Vector4.Zero;
            List<byte> tmp = new List<byte>();
           
            for (int x = xMin; x <= xMin + 1; x++)
            {
                for (int y = yMin; y <= yMin + 1; y++)
                {
                    for (int z = zMin; z <= zMin + 1; z++)
                    {
                        if (x < m_Size && y < m_Size && z < m_Size
                            &&
                            x > -1 && y > -1 && z > -1
                            )
                        {


                            if (m_data[x, y, z] > 0)
                            {
                                byte val=m_data[x, y, z];
                                
                                resD=resD+mat.MaterialColors[Count++].ToVector4();
                                if (Count>=mat.MaterialColors.Length)
                                {
                                    Count = 0; 
                                }
                            }
                        }
                    }
                }
            }
            resD /= 8;
            if (resD !=Vector4.Zero)
            {
                res = new Color(resD);
            }
            return res;

        }
    }
}
